#pragma once
#ifndef __GLF_DEBUGGER_H__
#define __GLF_DEBUGGER_H__

#include <glf/config.h>

#if GLF_ENABLE_DEBUGGER

#include <glf/core/thread.h>
#include <glf/core/mutex.h>
#include <glf/core/event.h>

#include <glf/debugger/packet.h>
#include <glf/debugger/debuggerAllocator.h>
#include <cstdarg>
#include <vector>
#include <map>

#ifndef GLF_DEBUGGER_PORT
#    define GLF_DEBUGGER_PORT 7940
#endif

#ifndef GLF_DEBUGGER_IDENTIFIER
#    define GLF_DEBUGGER_IDENTIFIER "Default Identifier"
#endif

#ifndef GLF_DEBUGGER_PC_SYNCHRONIZATION_FOLDER
#    define GLF_DEBUGGER_PC_SYNCHRONIZATION_FOLDER ""
#endif

#ifndef GLF_DEBUGGER_RESOURCE_SUBFOLDER
#    define GLF_DEBUGGER_RESOURCE_SUBFOLDER ""
#endif

#ifndef GLF_DEBUGGER_MAXIMUM_THREAD_COUNT
#    define GLF_DEBUGGER_MAXIMUM_THREAD_COUNT	32
#endif

namespace glf
{
namespace debugger
{

void* Alloc(size_t);
void  Free(void*);

class CNetInterface;
class CNetSocket;
class Debugger;

class Module
{
public:
	Module(const char* name, const char* modulePath = "");
	virtual ~Module();

	Debugger* GetDebugger() const { return mDebugger; }
	const stringdbg& GetName() const { return mName; }
	const stringdbg& GetModulePath() const { return mModulePath; }
	void Send(PacketWriter& writer);

	virtual void UpdateFrame() {}
	virtual void Parse(int type, PacketReader& in) {}
	virtual void ConnectionOpened() {}
	virtual void ConnectionClosed() {}
	virtual void DebuggerDestroyed() {}

protected:
	PacketWriter& GetPacketWriter();

private:
	stringdbg mName;
	stringdbg mModulePath;
	Debugger* mDebugger;

	friend class Debugger;
};

class ScopeMutex
{
public:
	ScopeMutex();
	~ScopeMutex();
};

struct SymbolInfo
{
	stringdbg FuncName;
	stringdbg FileName;
	uint Line;
};

struct ReportInfo
{
	stringdbg mName;
};

class CommonModule;

#define GLF_DEBUGGER_DECLARE_ALLOCATOR \
	static void* operator new(size_t size) { return Alloc(size); } \
	static void operator delete(void *p) { Free(p); } \


class Debugger : public EventReceiver 
{
	GLF_DECLARE_SINGLETON_PLACEMENT_NEW(Debugger)
public:
	~Debugger();

	bool Init(int rendererType);
	bool WaitForConnection(uint timeOutSeconds);
	bool IsConnected() const;
	bool GetHostAddress(char* buffer, uint bufferLen) const;
	ushort GetListeningPort() const;
	const char* GetRendererName() const;
	const char* GetPlatformName() const;

	void RegisterModule(Module* module);
	void UnregisterModule(Module* module);

	void Write(Module* module, PacketWriter& writer);

	void Update();
	void UpdateFrame();

private:
	Debugger();
	
	void InitSockets();
	void DeinitSockets();
	void Listen();
	
	void Close();

	bool OnEvent(const CoreEvent& evt);

	void GetDeviceInfos(char* buffer, uint length);
	void CheckDebuggerMultiCast();
	PacketWriter& GetPacketWriter() { return mPacketWriter; }

	CNetInterface* mNetMulticast;
	CNetInterface* mNet;
	CNetSocket* mNetSocket;
	PacketWriter mPacketWriter;
	int mRendererType;

 	typedef std::map<stringdbg, 
                     Module*, 
                     std::less<stringdbg>,
                     DebuggerAllocator<std::pair<const stringdbg, Module*> > 
                    > ModuleMap;
	ModuleMap mModules;
	int mPort;

	friend class Module;
	friend class CommonModule;
};

class CommonModule : public Module
{
	GLF_DECLARE_SINGLETON_WITH_DEP(CommonModule, Debugger)
public:
	~CommonModule();	

	virtual void Parse(int type, PacketReader& in);

	GLF_DEBUGGER_DECLARE_ALLOCATOR

#if defined(WIN32) && !defined(GLF_PLATFORM_WP8)
	static uint GetCallStack(unsigned long* callStack, uint size, unsigned long* hash = NULL);
	static bool ResolveFunctionSymbol(unsigned long address, SymbolInfo& symbolInfo);
#endif

	void SendCallStackSymbols(const stringdbg& destModuleId, const stringdbg& tag, uint depth, unsigned long* callStack);
	void GenerateReport(const ReportInfo& info);
	void ShowDebugger();

private:
	CommonModule();
	
	enum MessageType
	{
		// Debugger to Glf
		MESSAGE_REQUEST_CALLSTACK_SYMBOLS,
		MESSAGE_REQUEST_DEVICE_INFOS,
		MESSAGE_REQUEST_MODULE_LIST,
		MESSAGE_NOTIFY_DEBUGGER_READY,

		// Glf to Debugger
		MESSAGE_DEVICE_INFOS,
		MESSAGE_HANDLE_KEY_EVENT,
		MESSAGE_CALLSTACK_SYMBOLS,
		MESSAGE_COLOR_BUFFER,
		MESSAGE_DEPTH_BUFFER,
		MESSAGE_GENERATE_REPORT,
		MESSAGE_MODULE_LIST,
		MESSAGE_SHOW_DEBUGGER
	};

	virtual void UpdateFrame();

	void SendFrameBuffer(bool depthBuffer);
	void CaptureScreenBuffer(uint& width, uint& height, bool depth);
	void HandleKeyEvent(int keyCode);

	std::vector<uchar, DebuggerAllocator<uchar> > mFrameBuffer;
};

inline bool Debugger::IsConnected() const
{
	return mNetSocket != NULL;
}

void SpawnRemoteDebugger();

// Set relative path from app to glf root, ie: "./lib/glf"
void SetGlfRootPath(const char* path);

} // end namespace debugger
} // end namespace glf

#endif // GLF_ENABLE_DEBUGGER

#endif //__GLF_DEBUGGER_H__
